package cn.itcast.nems.order.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.itcast.ic.common.bean.BizError;
import cn.itcast.ic.common.exception.BizException;
import cn.itcast.ic.common.exception.v2.BizExceptionProducer;
import cn.itcast.nems.base.api.ClazzApi;
import cn.itcast.nems.base.dto.ClazzDTO;
import cn.itcast.nems.common.account.AccountHolder;
import cn.itcast.nems.common.account.AccountInfo;
import cn.itcast.nems.common.bean.VoucherType;
import cn.itcast.nems.common.constant.RefundStateEnum;
import cn.itcast.nems.common.enumeration.BizNumberTypeEnum;
import cn.itcast.nems.common.enumeration.NemsErrorBodyEnum;
import cn.itcast.nems.common.service.BizNumberService;
import cn.itcast.nems.common.util.NumberUtil;
import cn.itcast.nems.common.utils.BigDecimalUtil;
import cn.itcast.nems.journal.service.JournalTransferLineService;
import cn.itcast.nems.oldems.api.StudentApi;
import cn.itcast.nems.oldems.dto.Refund;
import cn.itcast.nems.oldems.dto.StudentDto;
import cn.itcast.nems.oldems.dto.oldapi.EmsApiResult;
import cn.itcast.nems.oldems.service.sync.OldEmsSyncService;
import cn.itcast.nems.order.dao.dataobject.OrderRefundDO;
import cn.itcast.nems.order.dao.dataobject.RefundClazzInfosDO;
import cn.itcast.nems.order.dao.entity.*;
import cn.itcast.nems.order.dao.mapper.*;
import cn.itcast.nems.order.dto.OrderEventTypeEnum;
import cn.itcast.nems.order.dto.OrderRefundDTO;
import cn.itcast.nems.order.service.OrderEventService;
import cn.itcast.nems.order.service.OrderRefundService;
import cn.itcast.nems.studentrecord.constant.ReferenceTypeEnum;
import cn.itcast.nems.studentrecord.dao.entity.JournalTransferLine;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecord;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecordStudentRelationship;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordFundFlowDTO;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordRefundDTO;
import cn.itcast.nems.studentrecord.enumeration.FundFlowPaymentTypeEnum;
import cn.itcast.nems.studentrecord.service.studentrecord.StudentRecordFundFlowService;
import cn.itcast.nems.studentrecord.service.studentrecord.StudentRecordService;
import cn.itcast.nems.studentrecord.service.studentrecord.StudentRecordStudentRelationshipService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

import static cn.itcast.nems.journal.util.TransferUtils.formatPrice;

/**
 * <p>
 * 退费单-头 服务实现类
 * </p>
 *
 * @author liyong
 * @since 2023-08-31
 */
@Slf4j
@Service
@AllArgsConstructor
public class OrderRefundServiceImpl extends ServiceImpl<OrderRefundMapper, OrderRefund> implements OrderRefundService {
    private final OrderMapper orderMapper;
    private final OrderLineMapper orderLineMapper;
    private final OrderEventService orderEventService;
    private final OrderRefundMapper orderRefundMapper;
    private final OrderRefundLineMapper orderRefundLineMapper;
    private final OrderPaymentFlowMapper orderPaymentFlowMapper;
    private final BizNumberService bizNumberService;
    private final StudentRecordService studentRecordService;
    private final ClazzApi clazzApi;
    private final StudentRecordFundFlowService studentRecordFundFlowService;
    private final OldEmsSyncService oldEmsSyncService ;
    private final JournalTransferLineService journalTransferLineService;
    private final StudentRecordStudentRelationshipService studentRecordStudentRelationshipService;
    private final StudentApi studentApi ;

    @Override
    public List<Map<String, Object>> listOrderRefund(String studentRecordId) {
        return orderRefundMapper.listOrderRefund(studentRecordId);
    }

    @Override
    public IPage<OrderRefundDO> listPage(Page<OrderRefundDO> page, OrderRefundDO condition) {
        if (!AccountHolder.hasEmployeeGroups()) {
            return null;
        }
        condition.setEmployeeGroups(AccountHolder.getEmployeeGroups());
        IPage<OrderRefundDO> pageList = orderRefundMapper.listPage(page, condition);
        pageList.getRecords().stream().map(item -> {
            String studentRecordId = item.getStudentRecordId();
            List<RefundClazzInfosDO> refundClazzInfosDOList = studentRecordService.listRefundClazzInfos(studentRecordId);
            // 退费班级信息
            item.setRefundClazzInfosDOList(refundClazzInfosDOList);
            return item;
        }).collect(Collectors.toList());
        return pageList;
    }

    @Override
    public Map<String, Object> orderRefundInfo(String orderRefundId) {
        return orderRefundMapper.orderRefundInfo(orderRefundId);
    }

    @Override
    public List<Map<String, Object>> paymentchannels(String schoolId) {
        return orderRefundMapper.paymentchannels(schoolId);
    }

    @Override
    public List<Map<String, Object>> incomeexpensestypes() {
        return orderRefundMapper.incomeexpensestypes();
    }

    /**
     * 退费单退费
     *
     * @param orderRefundDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OrderRefund createOrderRefund(OrderRefundDTO orderRefundDTO) {
        // 1.添加退费
        OrderRefund refund = this.createRefund(orderRefundDTO);
        if (refund == null) {
            return null;
        }
        Integer amount = refund.getAmount();
        Integer charge = refund.getCharge();
        String studentRecordId = refund.getStudentRecordId();
        String id = refund.getId();
        LocalDateTime bizDate = refund.getBizDate();
        Integer clazzId = refund.getClazzId();
        String clazzName = refund.getClazzName();
        String paymentNo = StringUtils.hasText(refund.getPaymentNo()) ? refund.getPaymentNo() : refund.getNo();
        String refundChannel = refund.getRefundChannel();
        String paymentAccountCode = null;
        if (StringUtils.hasText(orderRefundDTO.getSchoolId())) {
            // 获取付款渠道code
            paymentAccountCode = orderRefundMapper.paymentchannel(orderRefundDTO.getSchoolId(), refundChannel);
        }
        /*
         * 学籍为“休学班级”且 对应班级为 【休学】， -- 需要排除休学归来的情况
         *
         * 1、检查“休学班级”的学籍是否存在休学凭单 ，  如果有取该凭单原学籍对应的班级
         * 2、不存在休学凭单 ，  取当前学籍对应班级
         */
        Refund requestParam = this.convertToRefund(refund, paymentAccountCode, orderRefundDTO.getSchoolId());
        StudentRecord studentRecord = studentRecordService.getById(studentRecordId);
        if (ReferenceTypeEnum.SUSPENSION.name().equals(studentRecord.getSource())) {
            ClazzDTO originClazzDTO  = clazzApi.queryByClassId(studentRecord.getClazzId()) ;
            cn.hutool.core.lang.Assert.notNull(originClazzDTO , BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST , "【新建退费单】学籍班级ID："+  studentRecord.getClazzId())) ;
            log.info("【新建退费单】学籍来源：{}，是否为休学班级：{}" ,  studentRecord.getSource() ,  originClazzDTO.getSuspensionFlag());
            final String journalLineId = this.journalTransferLineService.findLineIdByJobStudentRecordId(studentRecordId);
            if (originClazzDTO.getSuspensionFlag() && StringUtils.hasText(journalLineId)) {
                final JournalTransferLine line = this.journalTransferLineService.getById(journalLineId);
                StudentRecord suspensionOriginStudentRecord = studentRecordService.getById(line.getOriginStudentRecordId());
                cn.hutool.core.lang.Assert.notNull(
                        suspensionOriginStudentRecord,
                        BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "退费休学原学籍ID：" + line.getOriginStudentRecordId())
                );
                log.info("当前学籍ID：{}，班级ID：{},休学原学籍ID：{}, 原学籍对应班级ID：{}",
                        studentRecord.getId(), studentRecord.getClazzId(),
                        line.getOriginStudentRecordId(), suspensionOriginStudentRecord.getClazzId());
                requestParam.setJoinclassid(suspensionOriginStudentRecord.getId());
            }
        }
        List<StudentRecordStudentRelationship> relationships = studentRecordStudentRelationshipService.findByStudentRecordId(requestParam.getJoinclassid()) ;
        cn.hutool.core.lang.Assert.notEmpty(relationships , BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST , "学籍学生关关系-学籍ID："+ requestParam.getJoinclassid())) ;
        StudentDto studentDto = studentApi.findById(relationships.get(0).getStudentId())  ;
        // 摘要
        requestParam.setName(StringUtils.hasText(orderRefundDTO.getReasonDescription())? orderRefundDTO.getReasonDescription(): ("退费"+ "-"+ studentDto.getName()));
        // 调用老EMS接口 同步退费单
        EmsApiResult result = oldEmsSyncService.refund(
                requestParam,
                AccountHolder.getRequiredAccountInfo().getEmail()
        );
        log.info("【创建退费单】同步至老EMS响应正文：{}", result);
        String reasonDescription = refund.getReasonDescription();
        // 2.更新学籍相关逻辑 e2_student_record
        StudentRecordRefundDTO refundDTO = new StudentRecordRefundDTO();
        refundDTO.setReferenceType(ReferenceTypeEnum.REFUND);
        refundDTO.setStudentRecordId(studentRecordId);
        refundDTO.setReferenceId(id);
        refundDTO.setRefundAmount(amount);
        refundDTO.setBizDate(bizDate);
        refundDTO.setUpdateReceivable(Boolean.TRUE);
        studentRecordService.refund(refundDTO,true);
        // 3.生成-学籍资金流转明细 e2_student_record_fund_flow
        StudentRecordFundFlowDTO fundFlow = new StudentRecordFundFlowDTO();
        fundFlow.setStudentRecordId(studentRecordId);
        fundFlow.setClazzId(clazzId);
        fundFlow.setClazzName(clazzName);
        fundFlow.setReferenceType(ReferenceTypeEnum.REFUND);
        fundFlow.setBizDate(orderRefundDTO.getRefundDate().atStartOfDay());
        fundFlow.setReferenceId(id);
        fundFlow.setReferenceLineId(StringUtils.hasText(refund.getOrderRefundLineId()) ? refund.getOrderRefundLineId() : id);
        fundFlow.setPaymentType(FundFlowPaymentTypeEnum.REFUND);
        fundFlow.setPaymentNo(paymentNo);
        fundFlow.setAmount(-amount);
        fundFlow.setCharge(charge);
        fundFlow.setPaymentMethodCode("");
        fundFlow.setPaymentMethodName("");
        fundFlow.setPaymentAccountCode(paymentAccountCode);
        fundFlow.setPaymentAccountName(refundChannel);
        fundFlow.setRemark(reasonDescription);
        fundFlow.setUpdateStudentRecord(Boolean.FALSE);
        studentRecordFundFlowService.create(fundFlow);
        return refund;
    }

    /**
     * <p>
     * 生成同步EMS退费单参数对象
     * </p>
     *
     * @param orderRefund 退费表单数据
     * @param channelId 渠道ID  ， 对应老EMS paymentchanel表 id
     * @author zhouwei
     * @since 2024/3/25 3:07 PM
     **/
    private Refund convertToRefund(OrderRefund orderRefund ,String channelId , String schoolCode ) {
        Refund refund =new Refund() ;
        // 收入支出类别（退费方式）, IncomeExpensesType表id
        refund.setIncomeExpensestype(Integer.parseInt(orderRefund.getCostTypeId()));
        // 退费手续费, 单位:元
        refund.setRefundCommission(NumberUtil.convertToYuan(orderRefund.getCharge())) ;
        // 费用
        refund.setCost(NumberUtil.convertToYuan(-orderRefund.getAmount()));
        // 先存学籍ID ， 调用EMS接口之前会替换成 老EMS studentjoinclazz 表 id
        refund.setJoinclassid(orderRefund.getStudentRecordId());
        // 收据号
        refund.setAcquittanceno(orderRefund.getReceiptNo());
        // pos交易号，刷pos机的编号
        refund.setPosNo(orderRefund.getPosNo());
        // 贷款公司, 枚举参看备注
        refund.setFinanceCorporation(null);
        // 费用发生日期
        refund.setFinancetime(orderRefund.getBizDate());
        refund.setPaymentchannel(channelId);
        // 学生缴费（方式）如：现金，贷款，刷卡等；退费（类型）。student_cost_type表的name字段
        refund.setStudentCostType(orderRefund.getRefundTypeName());
        // 凭证号
        refund.setVoucherno(orderRefund.getPaymentNo());
        // 校区code
        refund.setSchoolCode(schoolCode);
        return   refund ;
    }

    /**
     * 退学退费
     *
     * @param orderRefundDTO
     * @return
     */
    @Override
    public OrderRefund createDropoutRefund(OrderRefundDTO orderRefundDTO) {
        return this.createRefund(orderRefundDTO);
    }

    @Override
    public OrderRefund createRefund(OrderRefundDTO orderRefundDTO) {
        String studentRecordId = orderRefundDTO.getStudentRecordId();
        StudentRecord studentRecord = studentRecordService.getById(studentRecordId);
        Assert.notNull(studentRecord, "学籍数据不存在");
        Integer clazzId = orderRefundDTO.getClazzId();
        Assert.hasText(studentRecordId, "studentRecordId 不能为空");
        Assert.notNull(clazzId, "clazzId 不能为空");
        Assert.hasText(orderRefundDTO.getCostTypeId(), "costTypeId 不能为空");
        Assert.notNull(orderRefundDTO.getRefundDate(), "refundDate 不能为空");
        Assert.notNull(orderRefundDTO.getAmount(), "amount 不能为空");
        Assert.notNull(orderRefundDTO.getBizDate(), "bizDate 不能为空");
        if (StringUtils.hasText(orderRefundDTO.getPaymentNo())) {
            Assert.isTrue(orderRefundDTO.getPaymentNo().length() <= 40, "支付单号不可大于40");
        }
        if (StringUtils.hasText(orderRefundDTO.getReceiptNo())) {
            Assert.isTrue(orderRefundDTO.getReceiptNo().length() <= 40, "收据号不可大于40");
        }
        if (StringUtils.hasText(orderRefundDTO.getPosNo())) {
            Assert.isTrue(orderRefundDTO.getPosNo().length() <= 40, "POS单号不可大于40");
        }
        if (StringUtils.hasText(orderRefundDTO.getReasonDescription())) {
            Assert.isTrue(orderRefundDTO.getReasonDescription().length() <= 50, "摘要不可大于50");
        }
        Integer amount = BigDecimalUtil.bigDecimalToCentInt(orderRefundDTO.getAmount().setScale(2, RoundingMode.HALF_UP));
        Integer charge = 0;
        if (orderRefundDTO.getCharge() != null && orderRefundDTO.getCharge() != BigDecimal.ZERO) {
            charge = BigDecimalUtil.bigDecimalToCentInt(orderRefundDTO.getCharge().setScale(2, RoundingMode.HALF_UP));
        }

        // 公式：已交金额 + 转班转入金额 - 已退费合计
        int paidAmountNew = studentRecord.getPaidAmount() + studentRecord.getTransferFromAmount() - studentRecord.getRefundAmount();
        if (!ReferenceTypeEnum.DROPOUT.name().equals(studentRecord.getSettlementReason())
                && studentRecord.getTransferOutAmount() != null) {
            paidAmountNew = paidAmountNew - studentRecord.getTransferOutAmount();
        }
        paidAmountNew = Math.max(paidAmountNew, 0);
        if (amount > paidAmountNew) {
            throw new BizException(BizError.BAD_REQUEST.getStatus(), BizError.BAD_REQUEST.getCode(), String.format("当前退费班级的已支付金额为%s元，退费金额不能超过此金额！", formatPrice(paidAmountNew)));
        }

        OrderRefund orderRefund = new OrderRefund();
        BeanUtils.copyProperties(orderRefundDTO, orderRefund);
        orderRefund.setId(UUID.randomUUID().toString().replace("-", ""));
        orderRefund.setNo(this.createRefundOrderNo());
        orderRefund.setAmount(amount);
        orderRefund.setRefundableAmount(amount);
        orderRefund.setCharge(charge);
        orderRefund.setCostTypeName(orderRefundMapper.incomeexpensestypeById(Integer.parseInt(orderRefundDTO.getCostTypeId())));
        String studentCostType = orderRefundMapper.studentCostType(Integer.parseInt(orderRefundDTO.getRefundTypeId()));
        orderRefund.setRefundTypeName(studentCostType);
        // refund_method
        orderRefund.setRefundMethod("OTHER");
        String orderId = studentRecordService.findOrderId(studentRecordId);
        Order order = null;
        if (StringUtils.hasText(orderId)) {
            order = this.orderMapper.selectById(orderId);
            orderRefund.setBusinessLineId(order.getBusinessLineId());
            orderRefund.setBusinessLineName(order.getBusinessLineName());
        } else {
            orderRefund.setBusinessLineId(0);
            orderRefund.setBusinessLineName("无订单");
        }
        // 退费单设置为“退费完成”
        orderRefund.setState(RefundStateEnum.POSTED.name());
        // 更加班级id换取班级名称
        ClazzDTO clazzDTO = clazzApi.queryByClassId(Integer.valueOf(orderRefundDTO.getClazzId()));
        if (clazzDTO != null) {
            orderRefund.setClazzName(clazzDTO.getClassName());
        }
//        String schoolId = orderRefundDTO.getSchoolId();
//        orderRefund.setCampusId(schoolId);
//        SchoolDTO schoolDTO = schoolApi.findById(schoolId);
//        if (schoolDTO != null) {
//            orderRefund.setCampusName(schoolDTO.getName());
//        }
        // 1.添加退费单-头 e2_order_refund
        orderRefundMapper.insert(orderRefund);
        // 2.退款单-行/明细 e2_order_refund_line
        String orderRefundLineId = this.createOrderRefundLine(orderRefund.getId(), orderId, amount, orderRefund.getClazzId(), orderRefund.getClazzName());
        orderRefund.setOrderRefundLineId(orderRefundLineId);
        // 更新订单相关逻辑
        this.updateOrder(orderRefundDTO.getSchoolId(), order, orderRefund, studentCostType);
        return orderRefund;
    }

    private String createOrderRefundLine(String orderRefundId, String orderId, Integer amount, Integer clazzId, String clazzName) {
        OrderRefundLine orderRefundLine = new OrderRefundLine();
        orderRefundLine.setOrderRefundId(orderRefundId);
        if (StringUtils.hasText(orderId)) {
            Page<OrderLine> orderLinePage = orderLineMapper.selectPage(new Page<>(0, 1), new QueryWrapper<OrderLine>().lambda().eq(OrderLine::getOrderId, orderId));
            if (orderLinePage.getTotal() > 0) {
                List<OrderLine> records = orderLinePage.getRecords();
                OrderLine orderLine = records.get(0);
                orderRefundLine.setProductId(orderLine.getProductId());
                orderRefundLine.setProductName(orderLine.getProductName());
            }
        } else {
            orderRefundLine.setProductId(Long.valueOf(clazzId));
            orderRefundLine.setProductName(clazzName);
        }
        orderRefundLine.setAmount(amount);
        final AccountInfo info = AccountHolder.getRequiredAccountInfo();
        if (info != null) {
            orderRefundLine.setCreatedBy(info.getEmail());
            orderRefundLine.setCreatedByName(info.getName());
        }
        orderRefundLineMapper.insert(orderRefundLine);
        return orderRefundLine.getId();
    }

    // 更新订单相关逻辑
    private void updateOrder(String schoolId, Order order, OrderRefund orderRefund, String studentCostType) {
        // 4.如果没有订单，订单相关数据不做处理
        if (order == null) {
            return;
        }
        String orderId = order.getId();
        Integer amount = orderRefund.getAmount();
        Integer charge = orderRefund.getCharge();
        String refundChannel = orderRefund.getRefundChannel();
        String paymentAccountCode = null;
        if (StringUtils.hasText(schoolId)) {
            // 获取渠道code
            paymentAccountCode = orderRefundMapper.paymentchannel(schoolId, refundChannel);
        }
        final Integer orderRefundAmount = order.getRefundAmount();
        // 1.更新订单已退金额总额。所有退费累加。
        this.updateOrder(orderId, orderRefundAmount + amount);
        // 3.销售订单-支付流水 e2_order_payment_flow
        OrderPaymentFlow orderPaymentFlow = new OrderPaymentFlow();
        orderPaymentFlow.setOrderId(orderId);
        orderPaymentFlow.setNo(this.bizNumberService.next(BizNumberTypeEnum.ORDER_PAYMENT_FLOW));
        orderPaymentFlow.setJournalNo(orderRefund.getReceiptNo());
        orderPaymentFlow.setAmount(-amount);
        orderPaymentFlow.setCharge(charge);
        orderPaymentFlow.setJournalType(VoucherType.REFUND.toString());
        orderPaymentFlow.setBizDate(orderRefund.getRefundDate().atStartOfDay());
        orderPaymentFlow.setPaymentMethodCode(orderRefund.getRefundTypeId());
        orderPaymentFlow.setPaymentMethodName(studentCostType);
        orderPaymentFlow.setPaymentAccountCode(paymentAccountCode);
        orderPaymentFlow.setPaymentAccountName(orderRefund.getRefundChannel());
        orderPaymentFlow.setRemark(orderRefund.getReasonDescription());
        orderPaymentFlowMapper.insert(orderPaymentFlow);
    }

    // 更新订单已退金额总额。所有退费累加。
    private void updateOrder(String orderId, Integer amount) {
        final Order order = new Order();
        order.setId(orderId);
        order.setRefundFlag(Boolean.TRUE);
        order.setRefundAmount(amount);
        this.orderMapper.updateById(order);
        OrderEvent eventEntity = new OrderEvent();
        eventEntity.setOrderId(orderId);
        eventEntity.setType(OrderEventTypeEnum.REFUNDED.getName());
        eventEntity.setDescription("订单退费");
        orderEventService.save(eventEntity);
    }

    @Override
    public List<Map<String, Object>> studentCostTypes() {
        return orderRefundMapper.studentCostTypes();
    }

    @Override
    public String createRefundOrderNo() {
        return this.bizNumberService.next(BizNumberTypeEnum.ORDER_REFUND);
    }

    @Override
    public cn.itcast.ic.common.bean.Page<Map> searchClazzes(Integer pageNumber, Integer pageSize, OrderRefundDO params) {
        IPage<Map> pageData = baseMapper.searchClazzes(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageNumber, pageSize), params);
        if (CollectionUtil.isEmpty(pageData.getRecords())) {
            return cn.itcast.ic.common.bean.Page.of(pageNumber, pageSize, 0, null);
        }
        return cn.itcast.ic.common.bean.Page.of(pageNumber, pageSize, (int) pageData.getTotal(), pageData.getRecords());
    }

    @Override
    public String findNameByCostTypeId(Integer costTypeId) {
        return orderRefundMapper.studentCostType(costTypeId);
    }

}
